home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2002 #11 / Amiga Plus CD - 2002 - No. 11.iso / Tools / Development / ncurses-5.3 / ncurses / tinfo / comp_parse.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-10-27  |  14.7 KB  |  502 lines

  1. /****************************************************************************
  2.  * Copyright (c) 1998-2001,2002 Free Software Foundation, Inc.              *
  3.  *                                                                          *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a  *
  5.  * copy of this software and associated documentation files (the            *
  6.  * "Software"), to deal in the Software without restriction, including      *
  7.  * without limitation the rights to use, copy, modify, merge, publish,      *
  8.  * distribute, distribute with modifications, sublicense, and/or sell       *
  9.  * copies of the Software, and to permit persons to whom the Software is    *
  10.  * furnished to do so, subject to the following conditions:                 *
  11.  *                                                                          *
  12.  * The above copyright notice and this permission notice shall be included  *
  13.  * in all copies or substantial portions of the Software.                   *
  14.  *                                                                          *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
  16.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
  17.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
  18.  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
  19.  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
  20.  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
  21.  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
  22.  *                                                                          *
  23.  * Except as contained in this notice, the name(s) of the above copyright   *
  24.  * holders shall not be used in advertising or otherwise to promote the     *
  25.  * sale, use or other dealings in this Software without prior written       *
  26.  * authorization.                                                           *
  27.  ****************************************************************************/
  28.  
  29. /****************************************************************************
  30.  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
  31.  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
  32.  ****************************************************************************/
  33.  
  34. /*
  35.  *    comp_parse.c -- parser driver loop and use handling.
  36.  *
  37.  *    _nc_read_entry_source(FILE *, literal, bool, bool (*hook)())
  38.  *    _nc_resolve_uses(void)
  39.  *    _nc_free_entries(void)
  40.  *
  41.  *    Use this code by calling _nc_read_entry_source() on as many source
  42.  *    files as you like (either terminfo or termcap syntax).  If you
  43.  *    want use-resolution, call _nc_resolve_uses().  To free the list
  44.  *    storage, do _nc_free_entries().
  45.  *
  46.  */
  47.  
  48. #include <curses.priv.h>
  49.  
  50. #include <ctype.h>
  51.  
  52. #include <tic.h>
  53. #include <term_entry.h>
  54.  
  55. MODULE_ID("$Id: comp_parse.c,v 1.53 2002/09/07 20:01:28 tom Exp $")
  56.  
  57. static void sanity_check(TERMTYPE *);
  58. NCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype) (TERMTYPE *) = sanity_check;
  59.  
  60. /****************************************************************************
  61.  *
  62.  * Entry queue handling
  63.  *
  64.  ****************************************************************************/
  65. /*
  66.  *  The entry list is a doubly linked list with NULLs terminating the lists:
  67.  *
  68.  *      ---------   ---------   ---------
  69.  *      |       |   |       |   |       |   offset
  70.  *        |-------|   |-------|   |-------|
  71.  *      |   ----+-->|   ----+-->|  NULL |   next
  72.  *      |-------|   |-------|   |-------|
  73.  *      |  NULL |<--+----   |<--+----   |   last
  74.  *      ---------   ---------   ---------
  75.  *          ^                       ^
  76.  *          |                       |
  77.  *          |                       |
  78.  *       _nc_head                _nc_tail
  79.  */
  80.  
  81. NCURSES_EXPORT_VAR(ENTRY *) _nc_head = 0;
  82. NCURSES_EXPORT_VAR(ENTRY *) _nc_tail = 0;
  83.  
  84. static void
  85. enqueue(ENTRY * ep)
  86. /* add an entry to the in-core list */
  87. {
  88.     ENTRY *newp = _nc_copy_entry(ep);
  89.  
  90.     if (newp == 0)
  91.     _nc_err_abort(MSG_NO_MEMORY);
  92.  
  93.     newp->last = _nc_tail;
  94.     _nc_tail = newp;
  95.  
  96.     newp->next = 0;
  97.     if (newp->last)
  98.     newp->last->next = newp;
  99. }
  100.  
  101. NCURSES_EXPORT(void)
  102. _nc_free_entries(ENTRY * headp)
  103. /* free the allocated storage consumed by list entries */
  104. {
  105.     ENTRY *ep, *next;
  106.  
  107.     for (ep = headp; ep; ep = next) {
  108.     /*
  109.      * This conditional lets us disconnect storage from the list.
  110.      * To do this, copy an entry out of the list, then null out
  111.      * the string-table member in the original and any use entries
  112.      * it references.
  113.      */
  114.     FreeIfNeeded(ep->tterm.str_table);
  115.  
  116.     next = ep->next;
  117.  
  118.     free(ep);
  119.     if (ep == _nc_head)
  120.         _nc_head = 0;
  121.     if (ep == _nc_tail)
  122.         _nc_tail = 0;
  123.     }
  124. }
  125.  
  126. static char *
  127. force_bar(char *dst, char *src)
  128. {
  129.     if (strchr(src, '|') == 0) {
  130.     size_t len = strlen(src);
  131.     if (len > MAX_NAME_SIZE)
  132.         len = MAX_NAME_SIZE;
  133.     (void) strncpy(dst, src, len);
  134.     (void) strcpy(dst + len, "|");
  135.     src = dst;
  136.     }
  137.     return src;
  138. }
  139.  
  140. NCURSES_EXPORT(bool)
  141. _nc_entry_match(char *n1, char *n2)
  142. /* do any of the aliases in a pair of terminal names match? */
  143. {
  144.     char *pstart, *qstart, *pend, *qend;
  145.     char nc1[MAX_NAME_SIZE + 2], nc2[MAX_NAME_SIZE + 2];
  146.  
  147.     n1 = force_bar(nc1, n1);
  148.     n2 = force_bar(nc2, n2);
  149.  
  150.     for (pstart = n1; (pend = strchr(pstart, '|')); pstart = pend + 1)
  151.     for (qstart = n2; (qend = strchr(qstart, '|')); qstart = qend + 1)
  152.         if ((pend - pstart == qend - qstart)
  153.         && memcmp(pstart, qstart, (size_t) (pend - pstart)) == 0)
  154.         return (TRUE);
  155.  
  156.     return (FALSE);
  157. }
  158.  
  159. /****************************************************************************
  160.  *
  161.  * Entry compiler and resolution logic
  162.  *
  163.  ****************************************************************************/
  164.  
  165. NCURSES_EXPORT(void)
  166. _nc_read_entry_source(FILE * fp, char *buf,
  167.               int literal, bool silent,
  168.               bool(*hook) (ENTRY *))
  169. /* slurp all entries in the given file into core */
  170. {
  171.     ENTRY thisentry;
  172.     bool oldsuppress = _nc_suppress_warnings;
  173.     int immediate = 0;
  174.  
  175.     if (silent)
  176.     _nc_suppress_warnings = TRUE;    /* shut the lexer up, too */
  177.  
  178.     _nc_reset_input(fp, buf);
  179.     for (;;) {
  180.     memset(&thisentry, 0, sizeof(thisentry));
  181.     if (_nc_parse_entry(&thisentry, literal, silent) == ERR)
  182.         break;
  183.     if (!isalnum(UChar(thisentry.tterm.term_names[0])))
  184.         _nc_err_abort("terminal names must start with letter or digit");
  185.  
  186.     /*
  187.      * This can be used for immediate compilation of entries with no
  188.      * use references to disk, so as to avoid chewing up a lot of
  189.      * core when the resolution code could fetch entries off disk.
  190.      */
  191.     if (hook != NULLHOOK && (*hook) (&thisentry))
  192.         immediate++;
  193.     else
  194.         enqueue(&thisentry);
  195.     }
  196.  
  197.     if (_nc_tail) {
  198.     /* set up the head pointer */
  199.     for (_nc_head = _nc_tail; _nc_head->last; _nc_head = _nc_head->last)
  200.         continue;
  201.  
  202.     DEBUG(1, ("head = %s", _nc_head->tterm.term_names));
  203.     DEBUG(1, ("tail = %s", _nc_tail->tterm.term_names));
  204.     }
  205. #ifdef TRACE
  206.     else if (!immediate)
  207.     DEBUG(1, ("no entries parsed"));
  208. #endif
  209.  
  210.     _nc_suppress_warnings = oldsuppress;
  211. }
  212.  
  213. NCURSES_EXPORT(int)
  214. _nc_resolve_uses(bool fullresolve)
  215. /* try to resolve all use capabilities */
  216. {
  217.     ENTRY *qp, *rp, *lastread = 0;
  218.     bool keepgoing;
  219.     int i, j, unresolved, total_unresolved, multiples;
  220.  
  221.     DEBUG(2, ("RESOLUTION BEGINNING"));
  222.  
  223.     /*
  224.      * Check for multiple occurrences of the same name.
  225.      */
  226.     multiples = 0;
  227.     for_entry_list(qp) {
  228.     int matchcount = 0;
  229.  
  230.     for_entry_list(rp) {
  231.         if (qp > rp
  232.         && _nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) {
  233.         matchcount++;
  234.         if (matchcount == 1) {
  235.             (void) fprintf(stderr, "Name collision between %s",
  236.                    _nc_first_name(qp->tterm.term_names));
  237.             multiples++;
  238.         }
  239.         if (matchcount >= 1)
  240.             (void) fprintf(stderr, " %s", _nc_first_name(rp->tterm.term_names));
  241.         }
  242.     }
  243.     if (matchcount >= 1)
  244.         (void) putc('\n', stderr);
  245.     }
  246.     if (multiples > 0)
  247.     return (FALSE);
  248.  
  249.     DEBUG(2, ("NO MULTIPLE NAME OCCURRENCES"));
  250.  
  251.     /*
  252.      * First resolution stage: compute link pointers corresponding to names.
  253.      */
  254.     total_unresolved = 0;
  255.     _nc_curr_col = -1;
  256.     for_entry_list(qp) {
  257.     unresolved = 0;
  258.     for (i = 0; i < qp->nuses; i++) {
  259.         bool foundit;
  260.         char *child = _nc_first_name(qp->tterm.term_names);
  261.         char *lookfor = qp->uses[i].name;
  262.         long lookline = qp->uses[i].line;
  263.  
  264.         foundit = FALSE;
  265.  
  266.         _nc_set_type(child);
  267.  
  268.         /* first, try to resolve from in-core records */
  269.         for_entry_list(rp) {
  270.         if (rp != qp
  271.             && _nc_name_match(rp->tterm.term_names, lookfor, "|")) {
  272.             DEBUG(2, ("%s: resolving use=%s (in core)",
  273.                   child, lookfor));
  274.  
  275.             qp->uses[i].link = rp;
  276.             foundit = TRUE;
  277.         }
  278.         }
  279.  
  280.         /* if that didn't work, try to merge in a compiled entry */
  281.         if (!foundit) {
  282.         TERMTYPE thisterm;
  283.         char filename[PATH_MAX];
  284.  
  285.         memset(&thisterm, 0, sizeof(thisterm));
  286.         if (_nc_read_entry(lookfor, filename, &thisterm) == 1) {
  287.             DEBUG(2, ("%s: resolving use=%s (compiled)",
  288.                   child, lookfor));
  289.  
  290.             rp = typeMalloc(ENTRY, 1);
  291.             if (rp == 0)
  292.             _nc_err_abort(MSG_NO_MEMORY);
  293.             rp->tterm = thisterm;
  294.             rp->nuses = 0;
  295.             rp->next = lastread;
  296.             lastread = rp;
  297.  
  298.             qp->uses[i].link = rp;
  299.             foundit = TRUE;
  300.         }
  301.         }
  302.  
  303.         /* no good, mark this one unresolvable and complain */
  304.         if (!foundit) {
  305.         unresolved++;
  306.         total_unresolved++;
  307.  
  308.         _nc_curr_line = lookline;
  309.         _nc_warning("resolution of use=%s failed", lookfor);
  310.         qp->uses[i].link = 0;
  311.         }
  312.     }
  313.     }
  314.     if (total_unresolved) {
  315.     /* free entries read in off disk */
  316.     _nc_free_entries(lastread);
  317.     return (FALSE);
  318.     }
  319.  
  320.     DEBUG(2, ("NAME RESOLUTION COMPLETED OK"));
  321.  
  322.     /*
  323.      * OK, at this point all (char *) references in `name' members
  324.      * have been successfully converted to (ENTRY *) pointers in
  325.      * `link' members.  Time to do the actual merges.
  326.      */
  327.     if (fullresolve) {
  328.     do {
  329.         TERMTYPE merged;
  330.  
  331.         keepgoing = FALSE;
  332.  
  333.         for_entry_list(qp) {
  334.         if (qp->nuses > 0) {
  335.             DEBUG(2, ("%s: attempting merge",
  336.                   _nc_first_name(qp->tterm.term_names)));
  337.             /*
  338.              * If any of the use entries we're looking for is
  339.              * incomplete, punt.  We'll catch this entry on a
  340.              * subsequent pass.
  341.              */
  342.             for (i = 0; i < qp->nuses; i++)
  343.             if (qp->uses[i].link->nuses) {
  344.                 DEBUG(2, ("%s: use entry %d unresolved",
  345.                       _nc_first_name(qp->tterm.term_names), i));
  346.                 goto incomplete;
  347.             }
  348.  
  349.             /*
  350.                * First, make sure there's no garbage in the
  351.                * merge block.  as a side effect, copy into
  352.                * the merged entry the name field and string
  353.                * table pointer.
  354.              */
  355.             _nc_copy_termtype(&merged, &(qp->tterm));
  356.  
  357.             /*
  358.              * Now merge in each use entry in the proper
  359.              * (reverse) order.
  360.              */
  361.             for (; qp->nuses; qp->nuses--)
  362.             _nc_merge_entry(&merged,
  363.                     &qp->uses[qp->nuses - 1].link->tterm);
  364.  
  365.             /*
  366.              * Now merge in the original entry.
  367.              */
  368.             _nc_merge_entry(&merged, &qp->tterm);
  369.  
  370.             /*
  371.              * Replace the original entry with the merged one.
  372.              */
  373.             FreeIfNeeded(qp->tterm.Booleans);
  374.             FreeIfNeeded(qp->tterm.Numbers);
  375.             FreeIfNeeded(qp->tterm.Strings);
  376.             qp->tterm = merged;
  377.             _nc_wrap_entry(qp, TRUE);
  378.  
  379.             /*
  380.              * We know every entry is resolvable because name resolution
  381.              * didn't bomb.  So go back for another pass.
  382.              */
  383.             /* FALLTHRU */
  384.           incomplete:
  385.             keepgoing = TRUE;
  386.         }
  387.         }
  388.     } while
  389.         (keepgoing);
  390.  
  391.     DEBUG(2, ("MERGES COMPLETED OK"));
  392.  
  393.     /*
  394.      * The exit condition of the loop above is such that all entries
  395.      * must now be resolved.  Now handle cancellations.  In a resolved
  396.      * entry there should be no cancellation markers.
  397.      */
  398.     for_entry_list(qp) {
  399.         for_each_boolean(j, &(qp->tterm)) {
  400.         if ((int) qp->tterm.Booleans[j] == CANCELLED_BOOLEAN)
  401.             qp->tterm.Booleans[j] = ABSENT_BOOLEAN;
  402.         }
  403.         for_each_number(j, &(qp->tterm)) {
  404.         if (qp->tterm.Numbers[j] == CANCELLED_NUMERIC)
  405.             qp->tterm.Numbers[j] = ABSENT_NUMERIC;
  406.         }
  407.         for_each_string(j, &(qp->tterm)) {
  408.         if (qp->tterm.Strings[j] == CANCELLED_STRING)
  409.             qp->tterm.Strings[j] = ABSENT_STRING;
  410.         }
  411.     }
  412.     }
  413.  
  414.     /*
  415.      * We'd like to free entries read in off disk at this point, but can't.
  416.      * The merge_entry() code doesn't copy the strings in the use entries,
  417.      * it just aliases them.  If this ever changes, do a
  418.      * free_entries(lastread) here.
  419.      */
  420.  
  421.     DEBUG(2, ("RESOLUTION FINISHED"));
  422.  
  423.     if (fullresolve)
  424.     if (_nc_check_termtype != 0) {
  425.         _nc_curr_col = -1;
  426.         for_entry_list(qp) {
  427.         _nc_curr_line = qp->startline;
  428.         _nc_set_type(_nc_first_name(qp->tterm.term_names));
  429.         _nc_check_termtype(&qp->tterm);
  430.         }
  431.         DEBUG(2, ("SANITY CHECK FINISHED"));
  432.     }
  433.  
  434.     return (TRUE);
  435. }
  436.  
  437. /*
  438.  * This bit of legerdemain turns all the terminfo variable names into
  439.  * references to locations in the arrays Booleans, Numbers, and Strings ---
  440.  * precisely what's needed.
  441.  */
  442.  
  443. #undef CUR
  444. #define CUR tp->
  445.  
  446. static void
  447. sanity_check(TERMTYPE * tp)
  448. {
  449.     if (!PRESENT(exit_attribute_mode)) {
  450. #ifdef __UNUSED__        /* this casts too wide a net */
  451.     bool terminal_entry = !strchr(tp->term_names, '+');
  452.     if (terminal_entry &&
  453.         (PRESENT(set_attributes)
  454.          || PRESENT(enter_standout_mode)
  455.          || PRESENT(enter_underline_mode)
  456.          || PRESENT(enter_blink_mode)
  457.          || PRESENT(enter_bold_mode)
  458.          || PRESENT(enter_dim_mode)
  459.          || PRESENT(enter_secure_mode)
  460.          || PRESENT(enter_protected_mode)
  461.          || PRESENT(enter_reverse_mode)))
  462.         _nc_warning("no exit_attribute_mode");
  463. #endif /* __UNUSED__ */
  464.     PAIRED(enter_standout_mode, exit_standout_mode);
  465.     PAIRED(enter_underline_mode, exit_underline_mode);
  466.     }
  467.  
  468.     /* we do this check/fix in postprocess_termcap(), but some packagers
  469.      * prefer to bypass it...
  470.      */
  471.     if (acs_chars == 0
  472.     && enter_alt_charset_mode != 0
  473.     && exit_alt_charset_mode != 0)
  474.     acs_chars = strdup(VT_ACSC);
  475.  
  476.     /* listed in structure-member order of first argument */
  477.     PAIRED(enter_alt_charset_mode, exit_alt_charset_mode);
  478.     ANDMISSING(enter_alt_charset_mode, acs_chars);
  479.     ANDMISSING(exit_alt_charset_mode, acs_chars);
  480.     ANDMISSING(enter_blink_mode, exit_attribute_mode);
  481.     ANDMISSING(enter_bold_mode, exit_attribute_mode);
  482.     PAIRED(exit_ca_mode, enter_ca_mode);
  483.     PAIRED(enter_delete_mode, exit_delete_mode);
  484.     ANDMISSING(enter_dim_mode, exit_attribute_mode);
  485.     PAIRED(enter_insert_mode, exit_insert_mode);
  486.     ANDMISSING(enter_secure_mode, exit_attribute_mode);
  487.     ANDMISSING(enter_protected_mode, exit_attribute_mode);
  488.     ANDMISSING(enter_reverse_mode, exit_attribute_mode);
  489.     PAIRED(from_status_line, to_status_line);
  490.     PAIRED(meta_off, meta_on);
  491.  
  492.     PAIRED(prtr_on, prtr_off);
  493.     PAIRED(save_cursor, restore_cursor);
  494.     PAIRED(enter_xon_mode, exit_xon_mode);
  495.     PAIRED(enter_am_mode, exit_am_mode);
  496.     ANDMISSING(label_off, label_on);
  497. #ifdef remove_clock
  498.     PAIRED(display_clock, remove_clock);
  499. #endif
  500.     ANDMISSING(set_color_pair, initialize_pair);
  501. }
  502.